home *** CD-ROM | disk | FTP | other *** search
/ Amiga Game-Power / Amiga Game-Power.iso / pd mix ii / sprite ed / popcolors / popcolours.c < prev    next >
C/C++ Source or Header  |  1994-05-20  |  16KB  |  639 lines

  1. /*:ts=8 */
  2. /*  >>      PopColours: The amazing colour-set control panel      << 
  3. **  >> From The Transactor - THE Magazine for Commodore Computing <<
  4. **  
  5. **   November 1986, Version 1.0
  6. **
  7. ** - uses proportional gadgets for setting R G B
  8. **   and allows alteration of any colour register
  9. ** - operates on the TOPMOST OR SECOND SCREEN, so
  10. **   lets you change the colours of any currently 
  11. **   executing program with its own screen
  12. ** - only opens up when selected, so it doesn't eat up
  13. **   screen space when you're not using it
  14. **
  15. **           >> THIS PROGRAM MAY BE FREELY DISTRIBUTED <<
  16. **
  17. ** (c) 1986, AHA! (Acme Heuristic Applications!)
  18. **
  19. ** Written by Chris Zamara and Nick Sullivan
  20. */
  21.  
  22. #include <exec/types.h>
  23. #include <intuition/intuition.h>
  24. #include <graphics/gfx.h>
  25. #include <graphics/view.h>
  26.  
  27. /* defines for defaults, positioning, sizes, etc. */
  28. #define SCREEN2INIT FALSE /* default to topmost screen            */
  29. #define GADGHEIGHT 10L    /* height of proportional RGB gadgets        */
  30. #define RTOP 15L    /* y position of top of Red gadget        */
  31. #define GTOP 28L    /* " " " Green gadget                */
  32. #define BTOP 41L    /* " " " Blue gadget                */
  33. #define CVAL 177L    /* x position of colour values            */
  34. #define CPOSX 195L    /* x position of colour register indicator    */
  35. #define CPOSY 63L    /* y position of " " ...            */
  36. #define RPOSX 115L    /* x position of Register text            */
  37. #define SCRPOSX 15L    /* x position of screen indicator        */
  38. #define SCRPOSY 63L    /* y position of screen indicator        */
  39. #define UPGADGX 197L    /* x position of up gadget            */
  40. #define UPGADGY 10L     /* y position of up gadget            */
  41. #define DNGADGX 197L    /* x position of down gadget            */
  42. #define DNGADGY 32L     /* y position of down gadget            */
  43. #define COLORGADGET  1    /* ID for RGB gadgets                */
  44. #define UPGADGET     2    /* ID for up gadget                */
  45. #define DOWNGADGET   3    /* ID for down gadget                */
  46. #define SCREENGADGET 4    /* ID for down gadget                */
  47. #define ARROWDELAY 5    /* # of ticks before repeating up/down        */
  48. #define DELAY 100000    /* pause for "no 2nd scrn" message         */
  49. #define POTINC 0x1111    /* amount added to each colour pot per click    */
  50. #define RTEXT (RTOP+(GADGHEIGHT >> 1)+2) /* gadget text position    */
  51. #define GTEXT (GTOP+(GADGHEIGHT >> 1)+2)
  52. #define BTEXT (BTOP+(GADGHEIGHT >> 1)+2)
  53.  
  54. /* global structure declarations */
  55. struct IntuitionBase *IntuitionBase;
  56. struct GfxBase *GfxBase;
  57. struct ViewPort *vp;
  58. struct RastPort *rp;
  59. struct IntuiMessage *GetMsg(), *message;
  60. struct Window *OpenWindow(), *gwind, *SmallWind;
  61. struct Screen *TheScreen;
  62.  
  63. /* function declarations */
  64. APTR   OpenLibrary();
  65. USHORT GetRGB4();
  66. USHORT GadgPressed();
  67. BOOL   BigWindow();
  68.  
  69. /* gadget structures for colour controls */
  70. struct Gadget gadgR, gadgG, gadgB;
  71.  
  72. /* gadget structures for up/down arrows and screen selector */
  73. struct Gadget gadgUp, gadgDown, gadgScreen;
  74.  
  75. /* define NewWindow structure for our window */
  76. struct NewWindow GadgWind = {
  77.     400, 16, 233, 72,    /* left, top, width, height    */
  78.     -1, -1,            /* use screen colours        */
  79.     GADGETDOWN        /* IDCMP flags            */
  80.       | GADGETUP
  81.       | MOUSEBUTTONS
  82.       | INTUITICKS
  83.       | INACTIVEWINDOW
  84.       | CLOSEWINDOW,
  85.     WINDOWDEPTH        /* window flags            */
  86.       | WINDOWCLOSE
  87.       | WINDOWDRAG
  88.       | INACTIVEWINDOW
  89.       | RMBTRAP
  90.       | ACTIVATE
  91.       | SMART_REFRESH,
  92.     &gadgUp,        /* first gadget in list        */
  93.     NULL,
  94.     (UBYTE *)"PopColours 1.0", /* window title        */
  95.     NULL,            /* ptr to screen         */
  96.     NULL,
  97.     0, 0, 0, 0,        /* sizing limits (non-resizable)*/
  98.     WBENCHSCREEN
  99. };
  100.  
  101. /* also make NewWindow structure for small window */
  102. struct NewWindow SWind;
  103.  
  104. /* PropInfo structures for control gadgets */
  105. struct PropInfo propinfR, propinfG, propinfB;
  106.  
  107. /* control knob Image structures */
  108. struct Image knobR, knobG, knobB;
  109.  
  110. /* co-ordinate of points of up/down arrows */
  111. SHORT UpXY[8]   = { 13,0, 25,17,  0,17, 13,0 };
  112. SHORT DownXY[8] = {  0,0, 25, 0, 13,17,  0,0 };
  113.  
  114. /* Border structure for Up-Arrow gadget */
  115. struct Border UpArrow = {
  116.     3, 2,
  117.     1, 0, JAM1,
  118.     4,
  119.     UpXY,
  120.     NULL
  121. };
  122.  
  123. /* Border structure for Down-Arrow gadget */
  124. struct Border DownArrow = {
  125.     3, 2,
  126.     1, 0, JAM1,
  127.     4,
  128.     DownXY,
  129.     NULL
  130. };
  131.  
  132. /* this next array is for fast'n'easy integer to ASCII conversion */
  133. char *cnum[32] = { "00", "01", "02", "03", "04", "05", "06", "07",
  134.            "08", "09", "10", "11", "12", "13", "14", "15",
  135.            "16", "17", "18", "19", "20", "21", "22", "23",
  136.            "24", "25", "26", "27", "28", "29", "30", "31",
  137.          };
  138.  
  139. USHORT colreg = 0;    /* current colour register         */
  140. BOOL Screen2Flag;    /* false=top screen, true=second screen */
  141. BOOL Old2Flag;        /* previous value of Screen2Flag        */
  142.  
  143.  
  144. main (argc, argv)
  145.  
  146. int argc;
  147. char *argv[];
  148. {
  149.  
  150.     /* open intuition and graphics libraries    */
  151.     if ( (IntuitionBase = (struct IntuitionBase *)
  152.                    OpenLibrary ("intuition.library", 0L) ) == NULL)
  153.         exit (0L);
  154.  
  155.     if ( (GfxBase = (struct GfxBase *)
  156.                     OpenLibrary ("graphics.library", 0L) ) == NULL) {
  157.         CloseLibrary (IntuitionBase);
  158.         exit (0L);
  159.     }
  160.  
  161.     /* second arg specifies start-up colour register */
  162.     if (argc == 2)
  163.         colreg = atoi(argv[1]);
  164.  
  165.     /* switch between small and big windows until big is closed */
  166.     do {
  167.         SmallWindow();
  168.     } while (BigWindow());
  169.  
  170.     /* close everything up before ending */
  171.     CloseLibrary(GfxBase);
  172.     CloseLibrary(IntuitionBase);
  173.  
  174. }
  175.  
  176.  
  177. BOOL BigWindow ()
  178. {
  179.  
  180. ULONG  msgclass;    /* message class from IDCMP            */
  181. USHORT msgcode;        /* message code from IDCMP            */
  182. APTR   IAddr;        /* pointer to gadget from IDCMP            */
  183. USHORT WhichGadg;    /* ID of selected gadget            */
  184. USHORT TickStart;    /* countdown for up/down arrow repeat delay    */
  185. BOOL   GadgSel = FALSE;    /* set when a colour gadget is selected        */
  186. BOOL   RegSel = FALSE;    /* set when up or down gadget is selected    */
  187. BOOL   window_still_open = TRUE;
  188. BOOL   window_active = TRUE;
  189.  
  190.     GadgSetup();    /* initialize gadget structures */
  191.  
  192.     Screen2Flag = SCREEN2INIT;
  193.     Old2Flag   = !SCREEN2INIT;
  194.  
  195.     /* now attempt to open the window containing the gadgets */
  196.     if ((gwind = OpenWindow(&GadgWind)) == NULL) {
  197.         CloseLibrary(GfxBase);
  198.         CloseLibrary(IntuitionBase);
  199.         exit(0L);
  200.     }
  201.  
  202.     /* get ptr to rastport for graphics routines    */
  203.     rp = gwind->RPort;
  204.  
  205.     /* set RGB gadget positions to current colours    */
  206.     SetColrs();
  207.  
  208.     /* put up labels on window display        */
  209.     WinText();
  210.  
  211.     /* put up colour register number indicator    */
  212.     RegIndicate();
  213.  
  214.     /*** main event loop ***/
  215.     while (window_still_open && window_active) {
  216.  
  217.         /* wait politely, except when sliding a colour gadget */
  218.         if (! GadgSel)
  219.             Wait (1L << gwind->UserPort->mp_SigBit);
  220.  
  221.         while (message = GetMsg(gwind->UserPort)) {
  222.             /* get what we need from the message port */
  223.             msgclass = message->Class;
  224.             msgcode  = message->Code;
  225.             IAddr    = message->IAddress;
  226.             ReplyMsg(message); /* reply to message right away */
  227.  
  228.             /* now we can interpret the message */
  229.  
  230.             /* check for gadget selected */
  231.             if (msgclass == GADGETDOWN)
  232.                 WhichGadg = GadgPressed(IAddr, &GadgSel,
  233.                                 &RegSel, &TickStart);
  234.  
  235.             /* check if gadget released, even if off of gadget */
  236.             else if ( (msgclass == MOUSEBUTTONS
  237.                    && msgcode == SELECTUP)
  238.                   || msgclass == GADGETUP )
  239.                 GadgReleased(IAddr, &GadgSel, &RegSel,
  240.                         WhichGadg);
  241.  
  242.             /* auto-repeat arrow gadgets after delay */
  243.             else if (msgclass == INTUITICKS && RegSel) {
  244.                 /* wait ARROWDELAY ticks before repeating */
  245.                 if (TickStart++ > ARROWDELAY)
  246.                     UpdateColReg(WhichGadg);
  247.             }
  248.  
  249.             /* finish up if the close gadget is clicked */
  250.             else if (msgclass == CLOSEWINDOW)
  251.                 window_still_open = FALSE;
  252.  
  253.             /* exit if window made inactive */
  254.             else if (msgclass == INACTIVEWINDOW)
  255.                 window_active = FALSE;
  256.  
  257.         }
  258.         if (GadgSel)
  259.             /* set colour to pot values */
  260.             ReadGadg();
  261.  
  262.     }
  263.  
  264.     /* update NewWindow struct in case window has been moved */
  265.     GadgWind.LeftEdge = gwind->LeftEdge;
  266.     GadgWind.TopEdge = gwind->TopEdge;
  267.  
  268.     CloseWindow(gwind);
  269.  
  270.     return(window_still_open);
  271. }
  272.  
  273.  
  274. SmallWindow ()
  275. {
  276.  
  277. ULONG  msgclass;    /* message class from IDCMP            */
  278. BOOL   activated = FALSE;
  279.  
  280.     SWind = GadgWind; /* almost like big window, */
  281.     /* except for ... */
  282.     SWind.Height = 11;
  283.     SWind.IDCMPFlags = ACTIVEWINDOW;
  284.     SWind.FirstGadget = NULL;
  285.     SWind.Flags = WINDOWDEPTH | ACTIVEWINDOW;
  286.  
  287.     /* attempt to open the small window */
  288.     if ((SmallWind = OpenWindow(&SWind)) == NULL) {
  289.         CloseLibrary(GfxBase);
  290.         CloseLibrary(IntuitionBase);
  291.         exit(0L);
  292.     }
  293.  
  294.     Wait (1L << SmallWind->UserPort->mp_SigBit);
  295.     while (message = GetMsg(SmallWind->UserPort)) {
  296.         msgclass = message->Class;
  297.         ReplyMsg(message); /* reply to message right away */
  298.  
  299.         if (msgclass == ACTIVEWINDOW)
  300.             activated = TRUE;
  301.     }
  302.  
  303.     /* update NewWindow struct in case window has been moved */
  304.     GadgWind.LeftEdge = SmallWind->LeftEdge;
  305.     GadgWind.TopEdge = SmallWind->TopEdge;
  306.  
  307.     CloseWindow(SmallWind);
  308. }
  309.  
  310.  
  311. USHORT GadgPressed (IAddr, GadgSel, RegSel, TickStart)
  312. /* set flags and call appropriate routines when a gadget is clicked */
  313.  
  314. struct Gadget *IAddr;
  315. BOOL   *GadgSel, *RegSel;
  316. USHORT *TickStart;
  317. {
  318. USHORT WhichGadg;
  319.  
  320.     /* get gadget type */
  321.     WhichGadg = IAddr->GadgetID;
  322.  
  323.     /* rgb gadget? */
  324.     if (WhichGadg == COLORGADGET) {
  325.         *GadgSel = TRUE;
  326.         /* update settings if 2nd screen gone */
  327.         if (Screen2Flag    
  328.             && IntuitionBase->FirstScreen->NextScreen == NULL)
  329.             UpdateColReg(0);
  330.     }
  331.  
  332.     /* up/down gadget? */
  333.     else if (WhichGadg == UPGADGET || WhichGadg == DOWNGADGET) {
  334.         *RegSel = TRUE;
  335.         *TickStart = 0; /* start delay timer */
  336.         UpdateColReg(WhichGadg);
  337.     }
  338.  
  339.     /* Screen select gadget? */
  340.     else if (WhichGadg == SCREENGADGET) {
  341.         Screen2Flag = !Screen2Flag;
  342.         UpdateColReg(0);
  343.     }
  344.  
  345.     return (WhichGadg);
  346. }
  347.  
  348.  
  349. GadgReleased (IAddr, GadgSel, RegSel, WhichGadg)
  350. /* a gadget has been released - set flags and call appropriate routines */
  351.  
  352. struct Gadget *IAddr;
  353. BOOL   *GadgSel, *RegSel;
  354. USHORT WhichGadg;
  355. {
  356.  
  357.     if (WhichGadg == COLORGADGET) {
  358.         ReadGadg();
  359.         if ( ( ((struct PropInfo *)(IAddr->SpecialInfo))->Flags )
  360.              & KNOBHIT )
  361.             SetColrs(); /* re-position knob if it was moved */
  362.  
  363.         *GadgSel = FALSE;   /* colour gadget no longer selected */
  364.     }
  365.     else
  366.         *RegSel = FALSE;    /* ..or other gadget no longer selected */
  367. }
  368.  
  369.  
  370. ReadGadg ()
  371. /* read gadget pot values and set colour register 'colreg' accordingly */
  372.  
  373. {
  374. USHORT RedVal, GrnVal, BluVal;
  375.  
  376.     RedVal = propinfR.HorizPot / POTINC;
  377.     GrnVal = propinfG.HorizPot / POTINC;
  378.     BluVal = propinfB.HorizPot / POTINC;
  379.  
  380.     /* get viewport of topmost or second screen */
  381.     vp = &(TheScreen->ViewPort);
  382.  
  383.     /* change colour register */
  384.     SetRGB4(vp, (ULONG)colreg, (ULONG)RedVal,
  385.             (ULONG)GrnVal, (ULONG)BluVal
  386.            );
  387.  
  388.     PrintValues(RedVal, GrnVal, BluVal); /* numbers to right */
  389. }
  390.  
  391.  
  392. PrintValues (R, G, B)
  393. /* print colour values to right of gadgets */
  394.  
  395. USHORT R, G, B;
  396. {
  397.     SetAPen(rp, 1L);
  398.     Move(rp, CVAL, RTEXT);
  399.     Text(rp, cnum[R], 2L);
  400.     Move(rp, CVAL, GTEXT);
  401.     Text(rp, cnum[G], 2L);
  402.     Move(rp, CVAL, BTEXT);
  403.     Text(rp, cnum[B], 2L);
  404. }
  405.  
  406.  
  407. SetColrs ()
  408. /* set gadget pot values according to rgb in register 'colreg' */
  409.  
  410. {
  411. USHORT C0, R, G, B;
  412. ULONG hpR, hpG, hpB;
  413.  
  414.     /* get viewport of topmost screen */
  415.     ScreenPick(); /* select screen one or two */
  416.     vp = &(TheScreen->ViewPort);
  417.  
  418.     /* get rgb of specified colour register */
  419.     C0 = GetRGB4(vp->ColorMap, (ULONG)(colreg) );
  420.  
  421.     /* convert rgb to HorizPot values */
  422.     R = (C0 >> 8) & 0xF;
  423.     G = (C0 >> 4) & 0xF;
  424.     B = C0 & 0xF;
  425.     hpR = R * POTINC;
  426.     hpG = G * POTINC;
  427.     hpB = B * POTINC;
  428.  
  429.     /* change gadgets to reflect new colours */
  430.     ModifyProp(&gadgR, gwind, NULL,
  431.            (ULONG)propinfR.Flags, hpR, 0L,
  432.            (ULONG)propinfR.HorizBody, 0L
  433.           );
  434.     ModifyProp(&gadgG, gwind, NULL,
  435.            (ULONG)propinfG.Flags, hpG, 0L,
  436.            (ULONG)propinfG.HorizBody, 0L
  437.           );
  438.     ModifyProp(&gadgB, gwind, NULL,
  439.            (ULONG)propinfB.Flags, hpB, 0L,
  440.            (ULONG)propinfB.HorizBody, 0L
  441.           );
  442.  
  443.     PrintValues(R, G, B); /* colour numbers */
  444. }
  445.  
  446.  
  447. RegIndicate ()
  448. /* show colour register number */
  449.  
  450. {
  451. ULONG c;
  452.  
  453.     /* first print register number */
  454.     Move(rp, CPOSX, CPOSY);
  455.     SetAPen(rp, 1L);
  456.     Text(rp, cnum[colreg], 2L);
  457.  
  458.     /* now display a block in 'colreg' colour if changing this screen */
  459.     /* or background colour if changing another screen                */
  460.     c = (IntuitionBase->ActiveScreen == TheScreen) ? colreg : 0;
  461.     Move(rp, CPOSX+24, CPOSY);    /* move past text    */
  462.     SetDrMd(rp, INVERSVID);        /* inverse mode        */
  463.     SetAPen(rp, c);            /* set colour         */
  464.     Text(rp, " ", 1L);        /* print a space    */
  465.     SetDrMd(rp, JAM2);        /* set mode back to normal */
  466.     SetAPen(rp, 1L);
  467. }
  468.  
  469.  
  470. WinText ()
  471. /* put up various labels on window */
  472.  
  473. {
  474.     SetAPen(rp, 1L);
  475.  
  476.     Move(rp, 5L, RTEXT);
  477.     Text(rp, "R", 1L);
  478.  
  479.     Move(rp, 5L, GTEXT);
  480.     Text(rp, "G", 1L);
  481.  
  482.     Move(rp, 5L, BTEXT);
  483.     Text(rp, "B", 1L);
  484.  
  485.     Move(rp, RPOSX, CPOSY);
  486.     Text(rp, " Register", 9L);
  487.  
  488. }
  489.  
  490.  
  491. UpdateColReg (WhichGadg)
  492. /* increment or decrement colour register        */
  493. /* if WhichGadg is 0, just display current settings */
  494.  
  495. USHORT WhichGadg;
  496.  
  497. {
  498. USHORT numregs;
  499.  
  500.     /* get number of bitplanes of screen */
  501.     ScreenPick(); /* set 'TheScreen' */
  502.     numregs = 1 << ((TheScreen->ViewPort).RasInfo->BitMap->Depth);
  503.     /* (whew!) */
  504.  
  505.     /* increment or decrement colour register */
  506.     if (WhichGadg == UPGADGET)
  507.         colreg++;
  508.     else if (WhichGadg == DOWNGADGET)
  509.         colreg--;
  510.  
  511.     colreg = colreg % numregs;
  512.  
  513.     RegIndicate();
  514.     SetColrs();
  515. }
  516.  
  517.  
  518. ScreenPick ()
  519. /* select top screen if Screen2Flag false  **
  520. ** or second screen if true                **
  521. ** and update display if necesary          **
  522. */
  523.  
  524. {
  525. ULONG d;
  526.  
  527.     TheScreen = IntuitionBase->FirstScreen;
  528.  
  529.     Move(rp, SCRPOSX, SCRPOSY);
  530.     SetAPen(rp, 1L);
  531.     SetBPen(rp, 3L); /* print in different background colour */
  532.  
  533.     if (Screen2Flag) { 
  534.         if (TheScreen->NextScreen == NULL) {
  535.             /* no second screen, put up temp. message */
  536.             Screen2Flag = FALSE;
  537.             Text(rp, "(no 2nd scrn)", 13L);
  538.             /* delay the easy way (ok, so we hog a second) */
  539.             for(d=0; d<DELAY; d++)
  540.                 ;
  541.             Move(rp, SCRPOSX, SCRPOSY);
  542.             Text(rp, " TOP  SCREEN ", 13L);
  543.  
  544.         }
  545.         else {
  546.             /* set pointer to second screen */
  547.             TheScreen = TheScreen->NextScreen;
  548.             if (Old2Flag != Screen2Flag)
  549.                 Text(rp, "SECOND SCREEN", 13L);
  550.         }
  551.     }
  552.     if (!Screen2Flag && Old2Flag)
  553.         Text(rp, " TOP  SCREEN ", 13L);
  554.  
  555.     SetBPen(rp, 0L);
  556.     Old2Flag = Screen2Flag;
  557. }
  558.  
  559.  
  560. GadgSetup ()
  561. {
  562.     /* proportional gadget for red */
  563.     gadgR.NextGadget = NULL;
  564.     gadgR.LeftEdge = 16;
  565.     gadgR.TopEdge = RTOP;
  566.     gadgR.Width = 155;
  567.     gadgR.Height = GADGHEIGHT;
  568.     gadgR.Flags = GADGHNONE | GADGIMAGE;
  569.     gadgR.Activation = GADGIMMEDIATE | RELVERIFY;
  570.     gadgR.GadgetType = PROPGADGET;
  571.     gadgR.GadgetRender = (APTR)&knobR;
  572.     gadgR.SelectRender = NULL;
  573.     gadgR.GadgetText = NULL;
  574.     gadgR.MutualExclude = NULL;
  575.     gadgR.SpecialInfo = (APTR)&propinfR;
  576.     gadgR.GadgetID = COLORGADGET;
  577.     gadgR.UserData = NULL;
  578.  
  579.     /* up-arrow for changing colour register */
  580.     gadgUp.NextGadget = NULL;
  581.     gadgUp.LeftEdge = UPGADGX;
  582.     gadgUp.TopEdge = UPGADGY;
  583.     gadgUp.Width = 31;
  584.     gadgUp.Height = 21;
  585.     gadgUp.Flags = GADGHCOMP;
  586.     gadgUp.Activation = GADGIMMEDIATE | RELVERIFY;
  587.     gadgUp.GadgetType = BOOLGADGET;
  588.     gadgUp.GadgetRender = (APTR)↑
  589.     gadgUp.SelectRender = NULL;
  590.     gadgUp.GadgetText = NULL;
  591.     gadgUp.MutualExclude = NULL;
  592.     gadgUp.SpecialInfo = NULL;
  593.     gadgUp.GadgetID = UPGADGET;
  594.     gadgUp.UserData = NULL;
  595.  
  596.     /* top/second screen indicator/selector */
  597.     gadgScreen = gadgUp;
  598.     gadgScreen.LeftEdge = SCRPOSX;
  599.     gadgScreen.TopEdge = SCRPOSY-5;
  600.     gadgScreen.Width = 104;
  601.     gadgScreen.Height = 11;
  602.     gadgScreen.Activation = GADGIMMEDIATE;
  603.     gadgScreen.GadgetRender = NULL;
  604.     gadgScreen.GadgetID = SCREENGADGET;
  605.  
  606.     /* values needed for propinfo structure */
  607.     propinfR.Flags = FREEHORIZ | AUTOKNOB;
  608.     propinfR.HorizBody = 1 << 12;    /* body increment (1/16) */
  609.     /* propinf structures for R G B gadgets all alike */
  610.     propinfG = propinfR;
  611.     propinfB = propinfR;
  612.  
  613.     /* G and B gadgets same as R */
  614.     gadgG = gadgR;
  615.     gadgB = gadgR;
  616.     /* except for... */
  617.     gadgG.TopEdge = GTOP;
  618.     gadgB.TopEdge = BTOP;
  619.     gadgG.GadgetRender = (APTR)&knobG;
  620.     gadgB.GadgetRender = (APTR)&knobB;
  621.     gadgG.SpecialInfo = (APTR)&propinfG;
  622.     gadgB.SpecialInfo = (APTR)&propinfB;
  623.  
  624.     /* define down gadget in terms of up gadget */
  625.     gadgDown = gadgUp;
  626.     gadgDown.GadgetRender = (APTR)↓
  627.     gadgDown.LeftEdge = DNGADGX;
  628.     gadgDown.TopEdge  = DNGADGY;
  629.     gadgDown.GadgetID = DOWNGADGET;
  630.  
  631.     /* link all gadgets by pointers */
  632.     gadgUp.NextGadget = &gadgDown;
  633.     gadgDown.NextGadget = &gadgR;
  634.     gadgR.NextGadget = &gadgG;
  635.     gadgG.NextGadget = &gadgB;
  636.     gadgB.NextGadget = &gadgScreen;
  637.  
  638. }
  639.